<template>
  <div class="acm-root" ref="rootRef" v-bind="$attrs">
    <div
      v-if="visible && suggestions.length"
      class="acm-suggestions el-popper is-pure"
      role="listbox"
    >
      <ul class="acm-list">
        <li
          v-for="(item, idx) in suggestions"
          :key="idx + '-' + (item?.value ?? '')"
          class="ac-item"
          :class="{
            'is-selected': selectedSet.has(item?.value),
            'is-active': idx === highlightedIndex,
          }"
          role="option"
          @mousedown.prevent="selectAndKeepFocus(item)"
          @mousemove="highlightedIndex = idx"
        >
          <span class="label" :style="isSelectedStyle(item?.value)">{{
            item?.value
          }}</span>
        </li>
      </ul>
    </div>
    <el-input
      v-model="innerValue"
      :placeholder="placeholder"
      :type="multiLine ? 'textarea' : 'text'"
      :autosize="multiLine ? autosize : false"
      :rows="multiLine ? textareaRows as any : undefined"
      @focus="onFocus"
      :disabled="disabled"
      ref="inputRef"
    />
  </div>
</template>

<script setup lang="ts">
import { ref, watch, computed, onMounted, onUnmounted, nextTick } from 'vue';

interface SuggestionItem {
  value: string;
  // 允许透传额外字段
  [key: string]: any;
}

const props = withDefaults(
  defineProps<{
    modelValue: string | number | undefined | null;
    placeholder?: string;
    triggerOnFocus?: boolean;
    // 与 el-autocomplete 一致的签名
    fetchSuggestions?: (
      queryString: string,
      cb: (items: SuggestionItem[]) => void
    ) => void;
    // 结果数组（可选）：当没有提供 fetchSuggestions 时可直接使用
    items?: Array<string | SuggestionItem>;
    // 分隔符
    delimiter?: string;
    // 已选项颜色
    selectedColor?: string;
    // 是否去重
    deduplicate?: boolean;
    // 选择行为：auto(默认，末尾有分隔符则追加，否则替换最后片段) | append(总是追加) | replace(总是替换最后片段)
    selectionMode?: 'auto' | 'append' | 'replace';
    // 额外识别为分隔符的符号（可选），默认仅使用 delimiter 进行切分
    extraDelimiters?: string[];
    // 是否开启点击切换选择（已选则取消），默认开启
    toggleSelected?: boolean;
    // 是否多行展示（将输入切换为 textarea）。通常与 delimiter='\n' 搭配使用，实现每项换行展示
    multiLine?: boolean;
    // 多行时 textarea 自适应高度
    autosize?: boolean | { minRows?: number; maxRows?: number };
    // 多行时固定行数（当 autosize=false 时生效）
    textareaRows?: number;
    // 禁用时是否显示下拉框
    disabled?: boolean;
  }>(),
  {
    placeholder: '',
    triggerOnFocus: true,
    delimiter: ', ',
    selectedColor: '#16ada4',
    deduplicate: true,
    selectionMode: 'append',
    extraDelimiters: () => [],
    toggleSelected: true,
    multiLine: false,
    autosize: true,
    disabled: false,
    textareaRows: 3,
  }
);

const emit = defineEmits<{
  (e: 'update:modelValue', v: string): void;
  (e: 'change', v: string): void;
  (e: 'select', item: any): void;
}>();

const innerValue = ref<string>(props.modelValue?.toString?.() ?? '');
watch(
  () => props.modelValue,
  (v) => {
    const nv = v == null ? '' : String(v);
    if (nv !== innerValue.value) innerValue.value = nv;
  }
);
watch(innerValue, (v) => {
  emit('update:modelValue', v);
});

function escapeRegExp(str: string) {
  return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}

const delims = computed<string[]>(() => {
  const base = (props.delimiter ?? ', ').toString();
  const extras = Array.isArray(props.extraDelimiters)
    ? props.extraDelimiters.filter(Boolean)
    : [];
  return [base, ...extras].filter(Boolean);
});

function splitParts(v: string): string[] {
  if (!v) return [];
  // 多行模式：仅按换行符切分，忽略其它标点或分隔符
  if (props.multiLine) {
    return String(v)
      .split('\n')
      .map((s) => s.trim())
      .filter((s) => s.length > 0);
  }
  // 单行模式：按配置的分隔符数组切分
  const tokens = delims.value.length
    ? delims.value.map((d) => escapeRegExp(String(d)))
    : [','];
  const re = new RegExp(`(?:${tokens.join('|')})`, 'g');
  const normalized = (v || '').replace(re, '\n');
  return normalized
    .split('\n')
    .map((s) => s.trim())
    .filter((s) => s.length > 0);
}

const selectedSet = computed(() => new Set(splitParts(innerValue.value)));
function isSelectedStyle(val?: string) {
  if (!val) return undefined;
  return selectedSet.value.has(val)
    ? { color: props.selectedColor }
    : undefined;
}

// ========= 自定义下拉实现 =========
const rootRef = ref<HTMLElement | null>(null);
const inputRef = ref<any>(null);
const visible = ref(false);
const suggestions = ref<SuggestionItem[]>([]);
const highlightedIndex = ref(-1);

function lastFragment(text: string): string {
  if (!text) return '';
  if (props.multiLine) {
    const parts = String(text).split('\n');
    return parts[parts.length - 1]?.trim() ?? '';
  }
  const tokens = delims.value.length
    ? delims.value.map((d) => escapeRegExp(String(d)))
    : [','];
  const re = new RegExp(`(?:${tokens.join('|')})`, 'g');
  const normalized = text.replace(re, '\n');
  const parts = normalized.split('\n');
  return parts[parts.length - 1]?.trim() ?? '';
}

function doFetch(query: string) {
  if (typeof props.fetchSuggestions === 'function') {
    props.fetchSuggestions(query, (arr: any[]) => {
      suggestions.value = (Array.isArray(arr) ? arr : [])
        .map((x) =>
          typeof x === 'string' ? { value: x } : { value: x?.value ?? '', ...x }
        )
        .filter((x: any) => x.value);
    });
  } else {
    const list = Array.isArray(props.items) ? props.items : [];
    const q = (query || '').trim();
    suggestions.value = list
      .map((x: any) =>
        typeof x === 'string' ? { value: x } : { value: x?.value ?? '', ...x }
      )
      .filter((x: any) => x.value)
      .filter((x: any) => (q ? String(x.value).includes(q) : true));
  }
  highlightedIndex.value = suggestions.value.length ? 0 : -1;
}

function onFocus() {
  doFetch(lastFragment(''));
  visible.value = true;
}

function onInput(val: string) {
  // 输入变化时按最后片段搜索
  // doFetch(lastFragment(val || ''));
  visible.value = true;
}

function onArrowDown() {
  if (!visible.value) {
    visible.value = true;
    doFetch(lastFragment(innerValue.value || ''));
  } else if (suggestions.value.length) {
    highlightedIndex.value =
      (highlightedIndex.value + 1) % suggestions.value.length;
  }
}
function onArrowUp() {
  if (suggestions.value.length) {
    highlightedIndex.value =
      (highlightedIndex.value - 1 + suggestions.value.length) %
      suggestions.value.length;
  }
}
function onEnter() {
  if (visible.value && highlightedIndex.value > -1) {
    const it = suggestions.value[highlightedIndex.value];
    if (it) selectAndKeepFocus(it as Record<string, any>);
  } else {
    visible.value = false;
  }
}
function hide() {
  visible.value = false;
}

function selectAndKeepFocus(item: Record<string, any>) {
  handleSelect(item);
  // 选择后保留下拉以便连续多选，也可以按需关闭
  // visible.value = false;
}

const handleSelect = (item: Record<string, any>) => {
  const text = item?.value ?? '';
  if (!text) return;

  const current = innerValue.value || '';
  const parts = splitParts(current);
  // 若开启切换选择并且已存在，则取消选中（移除所有出现）
  if (props.toggleSelected && parts.includes(text)) {
    const removed = parts.filter((p) => p !== text);
    innerValue.value = removed.join(props.multiLine ? '\n' : props.delimiter);
    emit('select', item);
    emit('change', innerValue.value);
    return;
  }
  // 检测是否以分隔符结尾（兼容配置分隔符与常见符号），允许用户只敲逗号不敲空格
  const normCur = current.replace(/\s*$/, '');
  const candidates = delims.value
    .map((d) => String(d).replace(/\s*$/, ''))
    .filter(Boolean);
  const endsWithDelimiter = candidates.some((d) => normCur.endsWith(d));

  const mode = props.selectionMode || 'auto';
  // 简化：一次选择仅作为一个条目处理，自动换行由下方 join + 末尾换行控制
  const toAdd: string[] = [text];
  const shouldAppend =
    mode === 'append' ||
    (mode === 'auto' && (endsWithDelimiter || parts.length === 0));
  if (shouldAppend) {
    parts.push(...toAdd);
  } else {
    // 替换最后片段为第一条，其余依次追加
    const first = (toAdd[0] ?? '').toString();
    parts[parts.length - 1] = first;
    if (toAdd.length > 1)
      parts.push(...toAdd.slice(1).map((s) => s.toString()));
  }

  let newParts = props.deduplicate ? Array.from(new Set(parts)) : parts;
  // 多行模式下不自动补换行，光标停在内容末尾
  if (props.multiLine) {
    innerValue.value = newParts.join('\n');
  } else {
    innerValue.value = newParts.join(props.delimiter);
  }
  emit('select', item);
  emit('change', innerValue.value);
  // 选中后光标自动移动到内容末尾
  nextTick(() => {
    const el = inputRef.value?.$el?.querySelector('textarea') || inputRef.value?.$el?.querySelector('input');
    if (el) {
      el.selectionStart = el.value.length;
      el.selectionEnd = el.value.length;
      el.focus();
    }
  });
};

// 点击外部关闭
function onClickOutside(e: MouseEvent) {
  if (!rootRef.value) return;
  const target = e.target as Node;
  if (!rootRef.value.contains(target)) {
    hide();
  }
}
onMounted(() => {
  document.addEventListener('mousedown', onClickOutside);
});
onUnmounted(() => {
  document.removeEventListener('mousedown', onClickOutside);
});
</script>

<style scoped>
.acm-root {
  position: relative;
}
.acm-suggestions {
  position: absolute;
  z-index: 2000;
  min-width: 100%;
  margin-top: 4px;
  background: #fff;
  border: 1px solid var(--el-border-color);
  border-radius: 4px;
  box-shadow: var(--el-box-shadow-light);
  max-height: 240px;
  overflow: auto;
  bottom: 42px;
}
.acm-list {
  list-style: none;
  padding: 6px 0;
  margin: 0;
}
.ac-item {
  display: flex;
  align-items: center;
  padding: 6px 12px;
  line-height: 1.4;
  cursor: pointer;
}
.ac-item:hover,
.ac-item.is-active {
  background: var(--el-color-primary-light-9);
}
.ac-item .label {
  font-size: 14px;
}
.ac-item.is-selected .label {
  font-weight: 600;
}
</style>
